// serviceModel.go
package models

import (
	"context"
	"fmt"
	"log"
	"myk8s/common"
	"strings"

	"encoding/json"

	"github.com/tidwall/gjson"
	//"k8s.io/apimachinery/pkg/api/errors"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	yamlutil "k8s.io/apimachinery/pkg/util/yaml"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/intstr"

	"sigs.k8s.io/yaml"
)

type Service struct {
	ServiceName string `json:"serviceName"` //服务名称
	NameSpace   string `json:"nameSpace"`
	Labels      string `json:"labels"`
	SvcType     string `json:"svcType"`
	SvcIp       string `json:"svcIp"`
	SvcPort     string `json:"svcPort"`
	LanEndpoint string `json:"lanEndpoint"` //内部端点
	WanEndpoint string `json:"wanEndpoint"` //外部端点
	CreateTime  string `json:"createTime"`
}

type ServicePort struct {
	PortName   string `json:"portName"`
	SvcPort    int32  `json:"svcPort"`
	TargetPort int32  `json:"targetPort"`
}

func SvcList(kubeconfig, namespace, serviceName string, labelsKey, labelsValue string) ([]Service, error) {
	clientset := common.ClientSet(kubeconfig)
	if namespace == "" {
		//namespace = corev1.NamespaceDefault
		namespace = corev1.NamespaceAll
	}
	//var selector labels.Selector

	var listOptions = metav1.ListOptions{}
	if labelsKey != "" && labelsValue != "" {
		listOptions = metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelsKey, labelsValue)}
	}

	svcList, err := clientset.CoreV1().Services(namespace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list service error:%v\n", err)
	}

	//fmt.Println("svc count:", len(svcList.Items))
	var bbb = make([]Service, 0)
	for _, svc := range svcList.Items {
		//搜索
		if serviceName != "" {
			if !strings.Contains(svc.Name, serviceName) {
				continue
			}
		}
		//fmt.Printf("name: %s\n", svc.Name)
		var labelsStr string
		for kk, vv := range svc.ObjectMeta.Labels {
			labelsStr += fmt.Sprintf("%s:%s,", kk, vv)
		}
		if len(labelsStr) > 0 {
			labelsStr = labelsStr[0 : len(labelsStr)-1]
		}
		var wanEndPoint string
		svcType := fmt.Sprintf("%s", svc.Spec.Type)
		if svcType == "LoadBalancer" {
			wanEndPoint = fmt.Sprintf("%s:%d", svc.Status.LoadBalancer.Ingress[0].IP, svc.Spec.Ports[0].Port)
		}
		lanEndPoint := ""
		var svcPort string
		if len(svc.Spec.Ports) > 0 {
			vsapp := svc.Spec.Selector["app"]
			if vsapp == "" {
				vsapp = svc.Spec.Selector["k8s-app"]
			}
			for _, vv := range svc.Spec.Ports {
				//lanEndPoint += fmt.Sprintf("%s:%s:%s\n", svc.Spec.Selector["app"], svc.Spec.Ports[0].TargetPort.StrVal, svc.Spec.Ports[0].Protocol)
				//svcPort += fmt.Sprintf("%s:", svc.Spec.Ports[0].Port)
				var vport string
				if vv.TargetPort.Type == 0 {
					vport = fmt.Sprintf("%d", vv.TargetPort.IntVal)
				} else {
					vport = fmt.Sprintf("%s", vv.TargetPort.StrVal)
				}
				lanEndPoint += fmt.Sprintf("%s:%s:%s,", vsapp, vport, vv.Protocol)
				svcPort += fmt.Sprintf("%d,", vv.Port)
			}
			if len(svcPort) > 0 {
				svcPort = svcPort[0 : len(svcPort)-1]
			}
			lanEndPoint = lanEndPoint[0 : len(lanEndPoint)-1]
		}
		Items := &Service{
			ServiceName: svc.Name,
			NameSpace:   svc.Namespace,
			SvcType:     svcType,
			SvcIp:       svc.Spec.ClusterIP,
			Labels:      labelsStr,
			SvcPort:     svcPort,
			LanEndpoint: lanEndPoint,
			WanEndpoint: wanEndPoint,
			CreateTime:  svc.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

func SvcCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	serviceName := gp.Get("serviceName").String()
	nameSpace := gp.Get("nameSpace").String()
	svcType := gp.Get("svcType").String()
	deployName := gp.Get("deployName").String()
	isHeadless := gp.Get("isHeadless").Str

	var labelsMap = make(map[string]string)
	labelsMap["app"] = serviceName
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").Str] = vv.Get("value").Str
	}

	selectApp := serviceName
	if deployName != "" {
		selectApp = deployName
	}

	var serviceType corev1.ServiceType
	switch svcType {
	case "NodePort":
		serviceType = corev1.ServiceTypeNodePort
	case "LoadBalancer":
		serviceType = corev1.ServiceTypeLoadBalancer
	default:
		serviceType = corev1.ServiceTypeClusterIP
	}

	if isHeadless == "on" {
		serviceType = corev1.ServiceTypeClusterIP
	}

	svcInstance := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      serviceName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Spec: corev1.ServiceSpec{
			Selector: map[string]string{
				"app": selectApp,
			},
			Type: serviceType,
		},
	}
	if isHeadless == "on" {
		svcInstance.Spec.ClusterIP = corev1.ClusterIPNone
	}

	ports := gp.Get("ports").Array()
	var svcPorts = make([]corev1.ServicePort, 0)
	for _, vv := range ports {
		var svcProtocol corev1.Protocol
		if vv.Get("protocol").Str == "UDP" {
			svcProtocol = corev1.ProtocolUDP
		} else {
			svcProtocol = corev1.ProtocolTCP
		}
		svcPort := &corev1.ServicePort{
			Name:       vv.Get("portName").Str,
			Port:       int32(vv.Get("svcPort").Int()),
			Protocol:   svcProtocol,
			TargetPort: intstr.FromInt32(int32(vv.Get("targetPort").Int())),
		}
		svcPorts = append(svcPorts, *svcPort)
	}
	svcInstance.Spec.Ports = svcPorts

	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.CoreV1().Services(nameSpace).Create(context.TODO(), svcInstance, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	return nil
}

func SvcYamlModify(kubeconfig string, yamlData []byte) error {
	data, err := yamlutil.ToJSON(yamlData)
	if err != nil {
		return err
	}
	service := &corev1.Service{}
	err = json.Unmarshal(data, service)
	if err != nil {
		return err
	}

	namespace := service.ObjectMeta.Namespace
	serviceName := service.ObjectMeta.Name
	clientset := common.ClientSet(kubeconfig)
	_, err = clientset.CoreV1().Services(namespace).Update(context.TODO(), service, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	fmt.Println(namespace, serviceName)
	return err
}

func GetSvcYaml(kubeconfig, namespace, serviceName string) (string, error) {
	servicesClient := common.ClientSet(kubeconfig).CoreV1().Services(namespace)
	service, err := servicesClient.Get(context.TODO(), serviceName, metav1.GetOptions{})
	serviceUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(service)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(serviceUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}
